/*
 * MemCached.cpp
 *
 *  Created on: 2013. 10. 8.
 *      Author: the2
 */

#include "MemCached.h"
#include "MemResultSet.h"
#include "Ack.h"

namespace FriendsServer {

static void
StringToIntSplit(std::string & targetString, char separator, std::vector<int> & resultVector)
{
	int size = targetString.length();
	int start = 0;
	int stop = 0;

	start = targetString.find_first_not_of(separator);

	while ( (start >= 0) && (start < size) )
	{
		stop = targetString.find_first_of(separator, start);

		if ( (stop < 0) || (stop > size) )
		{
			stop = size;
		}

		resultVector.push_back(atoi(targetString.substr(start, stop - start).c_str()));

		start = targetString.find_first_not_of(separator, stop + 1);
	}
}

MemCachedData::MemCachedData()
{
}

MemCachedData::~MemCachedData()
{
}

bool MemCached::Init(const char *address)
{
	memcached_return_t rc;
	pMemc = memcached_create( NULL );
	pServer = memcached_servers_parse( address );

	rc = memcached_server_push( pMemc, pServer );
	if( rc != MEMCACHED_SUCCESS )
	{
		return false;
	}

	if( pServer->number_of_hosts > 1 )
	{
		memcached_behavior_set( pMemc, MEMCACHED_BEHAVIOR_KETAMA_HASH, 1 );
	}

	return true;
}

bool MemCached::Release()
{
	memcached_server_free( pServer );
	memcached_server_list_free( pServer );
	memcached_free( pMemc );

	pServer = NULL;
	pMemc = NULL;

	return true;
}

memcached_return_t MemCached::Set( const char *key, const char *value, const size_t size, time_t expiration )
{
	return memcached_set( pMemc, key, strlen(key), value, size, expiration, 0 );
}

char *MemCached::Get( const char *key, size_t *valuelen, uint32_t *flags, memcached_return_t *rc )
{
	return memcached_get( pMemc, key, strlen(key), valuelen, flags, rc );
}

memcached_return_t MemCached::MGet( const char* const *keys, const size_t *key_length, size_t number_of_keys )
{
	return memcached_mget( pMemc, keys, key_length, number_of_keys );
}

char* MemCached::Fetch( char *return_key, size_t *return_key_length, size_t *value_length, uint32_t *flags, memcached_return_t *rc )
{
	return memcached_fetch( pMemc, return_key, return_key_length, value_length, flags, rc);
}

memcached_return_t MemCached::Delete( const char *key, size_t key_length, time_t expiration )
{
	return memcached_delete( pMemc, key, key_length, expiration );
}

const char* MemCached::GetErrorString( memcached_return_t rc )
{
	return memcached_strerror( pMemc, rc );
}

memcached_return_t MemCached::FlushAll()
{
	return memcached_flush(this->pMemc, 0);
}

int
MemCached::SetMyInfo(std::shared_ptr<User> & clsuser)
{
	MemResultSet memResultSet;

	User::UserInfo & userinfo = clsuser->GetUserInfo();

	memResultSet.Init(1, 12);
	memResultSet.Add(userinfo.nickName.c_str());
	memResultSet.Add(userinfo.profileUrl.c_str());
	memResultSet.Add(std::to_string(userinfo.isHeartBlock).c_str());
	memResultSet.Add(std::to_string(userinfo.isInviteBlock).c_str());
	memResultSet.Add(std::to_string(userinfo.isProfileBlock).c_str());
	memResultSet.Add(std::to_string(userinfo.gender).c_str());
	memResultSet.Add(std::to_string(userinfo.isGenderOpen).c_str());
	memResultSet.Add(userinfo.birthday.c_str());
	memResultSet.Add(std::to_string(userinfo.isBirthdayOpen).c_str());
	memResultSet.Add(std::to_string(userinfo.lastLoginTime).c_str());
	memResultSet.Add(std::to_string(userinfo.unregister_time).c_str());
	memResultSet.Add(std::to_string(userinfo.shard_db_type).c_str());

	std::string memKey = __USER__ + std::to_string(clsuser->GetUserSeq());
	Memcache->Set(memKey.c_str(), memResultSet.m_Data, memResultSet.m_Offset, 0);
	return ACK_OK;
}


int
MemCached::GetInfoSocialMine(std::shared_ptr<User> & clsuser)
{
	MemResultSet memResultSet;

	User::UserInfo & userinfo = clsuser->GetUserInfo();

	std::string memKey = __USER__ + std::to_string(clsuser->GetUserSeq());

	time_t curtime = time(NULL);

	Database::ResultSet res = Database::Execute(1, "call sp_select_info_social_mine(", clsuser->GetUserSeq(), ")");
	if (0 != res.GetSQLError())
	{
		LOG(DEV, "sp_socialmyinfo errno : ", errno );
		return ACK_DB_ERROR;
	}

	if (res.end() == res.begin())
	{
		LOG(DEV, "sp_socialmyinfo result is null" );
		return ACK_EXIST_USER;
	}

	memResultSet.Init(res.GetRowCount(), 12);
	for(auto itr = res.begin(); itr != res.end(); itr++)
	{
		memResultSet.Add(userinfo.nickName.c_str());
		memResultSet.Add(userinfo.profileUrl.c_str());
		memResultSet.Add(itr->getString("is_heart_blocked").c_str());
		memResultSet.Add(itr->getString("is_invite_blocked").c_str());
		memResultSet.Add(itr->getString("is_profile_opened").c_str());
		memResultSet.Add(itr->getString("sex").c_str());
		memResultSet.Add(itr->getString("is_open_sex").c_str());
		memResultSet.Add(itr->getString("birthday").c_str());
		memResultSet.Add(itr->getString("is_open_birth").c_str());
		memResultSet.Add(std::to_string(static_cast<int>(curtime)).c_str());
		memResultSet.Add(std::to_string(userinfo.unregister_time).c_str());
		memResultSet.Add(itr->getString("shard_db_type").c_str());

		clsuser->SetMyRank(itr->getInt("my_rank"));
		clsuser->SetFriendRankCount(itr->getInt("friend_rank_count"));

		clsuser->SetRankConfirmTime( itr->getInt("week_rank_confirm") );

		clsuser->SetInviteCount( itr->getInt("invite_count") );
		clsuser->SetHeartCount( itr->getInt("heart_count"));

		Memcache->Set(memKey.c_str(), memResultSet.m_Data, memResultSet.m_Offset, 0);
	}

	while ( memResultSet.MoreRecord() )
	{
		userinfo.isHeartBlock		= atoi(memResultSet.ROW[2]);
		userinfo.isInviteBlock		= atoi(memResultSet.ROW[3]);
		userinfo.isProfileBlock		= atoi(memResultSet.ROW[4]);
		userinfo.gender				= atoi(memResultSet.ROW[5]);
		userinfo.isGenderOpen		= atoi(memResultSet.ROW[6]);
		userinfo.birthday			= memResultSet.ROW[7];
		userinfo.isBirthdayOpen		= atoi(memResultSet.ROW[8]);
		userinfo.lastLoginTime		= curtime;
		userinfo.unregister_time	= atoi(memResultSet.ROW[10]);
		userinfo.shard_db_type		= atoi(memResultSet.ROW[11]);
	}
	return ACK_OK;
}

int
MemCached::GetInfoSocialOther(std::shared_ptr<User> clsuser, const uint32_t friendSeq, User::UserInfo & userinfo)
{
	MemResultSet memResultSet;
	size_t valuelen;
	uint32_t flags;
	memcached_return_t rc;

	std::string memKey = __USER__ + std::to_string(friendSeq);
	memResultSet.m_pVal = memcached_get( pMemc, memKey.c_str(), strlen(memKey.c_str()), &valuelen, &flags, &rc );
	if( rc != MEMCACHED_SUCCESS )
	{
		Database::ResultSet res = Database::Execute(2, "call sp_select_info_social_other(", friendSeq, ")");
		if (0 != res.GetSQLError())
		{
			return ACK_DB_ERROR;
		}

		if (res.end() == res.begin())
		{
			return ACK_EXIST_USER;
		}

		memResultSet.Init(res.GetRowCount(), 12);
		for(auto itr = res.begin(); itr != res.end(); itr++)
		{
			memResultSet.Add(itr->getString("nickname").c_str());
			memResultSet.Add(itr->getString("profile_url").c_str());
			memResultSet.Add(itr->getString("is_heart_blocked").c_str());
			memResultSet.Add(itr->getString("is_invite_blocked").c_str());
			memResultSet.Add(itr->getString("is_profile_opened").c_str());
			memResultSet.Add(itr->getString("sex").c_str());
			memResultSet.Add(itr->getString("is_open_sex").c_str());
			memResultSet.Add(itr->getString("birthday").c_str());
			memResultSet.Add(itr->getString("is_open_birth").c_str());
			memResultSet.Add(itr->getString("last_login_time").c_str());
			memResultSet.Add(itr->getString("unregister_time").c_str());
			memResultSet.Add(itr->getString("shard_db_type").c_str());

			rc = Memcache->Set(memKey.c_str(), memResultSet.m_Data, memResultSet.m_Offset, 0);
		}
	}
	else
	{
		LOG(DEV, "Memcache->Get 성공");
		memResultSet.Init( memResultSet.m_pVal, valuelen );
	}

	while ( memResultSet.MoreRecord() )
	{
		userinfo.nickName = memResultSet.ROW[0];
		userinfo.profileUrl = memResultSet.ROW[1];
		userinfo.isHeartBlock = atoi(memResultSet.ROW[2]);
		userinfo.isInviteBlock = atoi(memResultSet.ROW[3]);
		userinfo.isProfileBlock = atoi(memResultSet.ROW[4]);
		userinfo.gender = atoi(memResultSet.ROW[5]);
		userinfo.isGenderOpen = atoi(memResultSet.ROW[6]);
		userinfo.birthday = memResultSet.ROW[7];
		userinfo.isBirthdayOpen = atoi(memResultSet.ROW[8]);
		userinfo.lastLoginTime = atoi(memResultSet.ROW[9]);
		userinfo.unregister_time = atoi(memResultSet.ROW[10]);
		userinfo.shard_db_type = atoi(memResultSet.ROW[11]);
	}

	if (0 != userinfo.unregister_time)
	{
		return clsuser->DeleteFriend(clsuser->GetUserInfo().shard_db_type, clsuser->GetUserSeq(), friendSeq);
	}

	return ACK_OK;
}

int
MemCached::GetScore(const uint32_t userSeq, MemCachedData::Score & score)
{
	size_t valuelen;
	uint32_t flags;
	memcached_return_t rc;

	std::string memKey = __SCORE__ + std::to_string(userSeq);
	char * value = memcached_get( pMemc, memKey.c_str(), strlen(memKey.c_str()), &valuelen, &flags, &rc );

	if(NULL != value)
	{
		memcpy( &score , value, sizeof(MemCachedData::Score) );
		free(value);
	}

	if( rc != MEMCACHED_SUCCESS )
	{
		Database::ResultSet res = Database::Execute(2, "call sp_select_info_user_score(", userSeq, ")");
		if (0 != res.GetSQLError())
		{
			LOG(DEV, "sp_score errno : ", errno );
			return ACK_DB_ERROR;
		}

		if (res.end() == res.begin())
		{
			LOG(DEV, "sp_score result is null" );
			return ACK_EXIST_USER;
		}

		for(auto itr = res.begin(); itr != res.end(); itr++)
		{
			score.lastweek = itr->getInt("wp_last_week");
			score.curweek = itr->getInt("wp_cur_week");
			score.reg_date = itr->getInt("wp_reg_date");
			score.last_reg_date = itr->getInt("wp_last_reg_date");
		}

		Memcache->Set(memKey.c_str(), reinterpret_cast<char*>(&score), sizeof(MemCachedData::Score), 0);
	}
	else
	{
		LOG(DEV, "Memcache->Get 성공");
	}

	return ACK_OK;
}

bool
MemCached::GetLocation(const uint32_t userSeq, MemCachedData::ServerLocation & serverLocation)
{
	size_t valuelen;
	uint32_t flags;
	memcached_return_t rc;

	std::string memKey = __LOCATION__ + std::to_string(userSeq);
	char * value = memcached_get( pMemc, memKey.c_str(), strlen(memKey.c_str()), &valuelen, &flags, &rc );

	if(NULL != value)
	{
		memcpy( &serverLocation , value, sizeof(MemCachedData::ServerLocation) );
		free(value);
	}

	if( rc != MEMCACHED_SUCCESS )
		return false;

	return true;
}

int
MemCached::GetMedal(const uint32_t userSeq, MemCachedData::Medal & medal)
{
	size_t valuelen;
	uint32_t flags;
	memcached_return_t rc;

	std::string memKey = __MEDAL__ + std::to_string(userSeq);
	char * value = memcached_get( pMemc, memKey.c_str(), strlen(memKey.c_str()), &valuelen, &flags, &rc );

	if(NULL != value)
	{
		memcpy( &medal , value, sizeof(MemCachedData::Medal) );
		free(value);
	}

	if( rc != MEMCACHED_SUCCESS )
	{
		Database::ResultSet res = Database::Execute(2, "select medal from info_user where user_seq = ", userSeq);
		if (0 != res.GetSQLError())
		{
			LOG(DEV, "select medal errno : ", errno );
			return ACK_DB_ERROR;
		}

		if (res.end() == res.begin())
		{
			LOG(DEV, "select medal result is null" );
			return ACK_EXIST_USER;
		}

		std::string strMedal;
		for(auto itr = res.begin(); itr != res.end(); itr++)
		{
			strMedal = itr->getString("medal");
		}

		std::vector<int> medalVector;
		StringToIntSplit(strMedal, ',', medalVector);

		auto iter = medalVector.begin();
		for (int i=0; iter != medalVector.end(); ++iter, i++)
		{
			switch(i)
			{
			case 0:
				medal.gold = medalVector[0];
				break;
			case 1:
				medal.silver = medalVector[1];
				break;
			case 2:
				medal.bronze = medalVector[2];
				break;
			}
		}

		Memcache->Set(memKey.c_str(), reinterpret_cast<char*>(&medal), sizeof(MemCachedData::Medal), 0);
	}
	else
	{
		LOG(DEV, "Memcache->Get 성공");
	}

	return ACK_OK;
}

}

